package com.yeskery.nut.extend.mcp.server;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.bean.FactoryBeanRegister;
import com.yeskery.nut.extend.mcp.annotation.McpResource;
import com.yeskery.nut.extend.mcp.annotation.McpResourceProperty;
import com.yeskery.nut.extend.mcp.core.Resource;
import com.yeskery.nut.extend.mcp.core.ResourceType;
import com.yeskery.nut.extend.mcp.core.schema.ResourceSchema;
import com.yeskery.nut.util.PathUtils;
import com.yeskery.nut.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * Mcp资源bean后置处理器
 * @author sprout
 * 2025-04-18 11:22
 */
public class McpResourceBeanPostProcessor extends BaseMcpBeanPostProcessor {

    /**
     * 构建Mcp工具bean后置处理器
     * @param mcpServerCapability mcp服务能力
     */
    public McpResourceBeanPostProcessor(McpServerCapability mcpServerCapability) {
        super(mcpServerCapability);
    }

    @Override
    protected Class<? extends Annotation> getMethodTargetAnnotation() {
        return McpResource.class;
    }

    @Override
    protected void doProcess(Object bean, Class<?> beanClass, Method method, ApplicationContext applicationContext, FactoryBeanRegister factoryBeanRegister) {
        Resource resource = createResource(method);
        List<String> parameterNames = new ArrayList<>();
        for (Parameter parameter : method.getParameters()) {
            Class<?> parameterType = parameter.getType();
            if (!isBasicType(parameterType)) {
                throw new McpServerException("Parameter Only Support Basic Type, Current Type[" + parameterType + "] Not Support.");
            }
            McpResourceProperty mcpResourceProperty = parameter.getAnnotation(McpResourceProperty.class);
            String name = parameter.getName();
            if (mcpResourceProperty != null) {
                if (!StringUtils.isBlank(mcpResourceProperty.value())) {
                    name = mcpResourceProperty.value();
                }
            }
            parameterNames.add(name);
        }
        McpResourceMetadata resourceMetadata = new McpResourceMetadata();
        resourceMetadata.setBean(bean);
        resourceMetadata.setMethod(method);
        resourceMetadata.setResource(resource);
        resourceMetadata.setParameterNames(parameterNames);
        String uri = resourceMetadata.isResource() ? ((ResourceSchema.Resource) resource.getResource()).getUri()
                : ((ResourceSchema.ResourceTemplate) resource.getResource()).getUriTemplate();
        resourceMetadata.setPathMetadata(PathUtils.parse(uri));
        getMcpServerCapability().addResourceMetadata(resourceMetadata);
    }

    /**
     * 创建资源
     * @param method 资源对象
     * @return 资源对象
     */
    private Resource createResource(Method method) {
        McpResource mcpResource = method.getAnnotation(McpResource.class);
        ResourceSchema.BaseResource schemaResource = getSchemaResource(mcpResource);
        Resource resource = new Resource();
        resource.setResource(schemaResource);
        return resource;
    }

    /**
     * 获取资源对象
     * @param mcpResource Mcp资源注解
     * @return 资源对象
     */
    private ResourceSchema.BaseResource getSchemaResource(McpResource mcpResource) {
        ResourceSchema.BaseResource schemaResource;
        if (mcpResource.type() == ResourceType.RESOURCE) {
            ResourceSchema.Resource resource = new ResourceSchema.Resource();
            resource.setUri(mcpResource.value());
            schemaResource = resource;
        } else {
            ResourceSchema.ResourceTemplate resourceTemplate = new ResourceSchema.ResourceTemplate();
            resourceTemplate.setUriTemplate(mcpResource.value());
            schemaResource = resourceTemplate;
        }
        if (!StringUtils.isBlank(mcpResource.mimeType())) {
            schemaResource.setMimeType(mcpResource.mimeType());
        } else {
            schemaResource.setMimeType(mcpResource.mediaType().getValue());
        }
        schemaResource.setName(mcpResource.name());
        schemaResource.setDescription(mcpResource.description());
        return schemaResource;
    }
}
